package netWork;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * 网络编程中Tcp回显服务器的实现,字节流
 */

public class TcpEchoServer {
    //网卡,相当于接口
    private ServerSocket serverSocket = null;

    //有三种实现的方式,这里是第一种,不过会出现一个服务器只能对应一个客户端的情况
    //可以用多线程解决这个问题
    public TcpEchoServer(int port) throws IOException {//这里的port是端口号
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");
        while(true){
            //接收客户端请求
            Socket clientSocket = serverSocket.accept();

            //进行连接
            processConnection(clientSocket);
        }

    }

    //针对一个连接,提供处理逻辑
    private void processConnection(Socket clientSocket) throws IOException {
        //先打印一下客户端的信息
        System.out.printf("[%s : %d] 客户端上线 !\n",clientSocket.getInetAddress(),clientSocket.getPort());

        //获取到socket中持有的对象
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用Scanner 包装一下 inputStream,就可以更方便的读取这里的请求数据了
            Scanner scanner = new Scanner(inputStream);//scanner必须要以 空格 \n等为标志符才能停止读取
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true){
                //1.读取请求并解析
                if(!scanner.hasNext()){
                    //如果 scanner 无法读取出数据,说明了客户端关闭了连接,导致服务器这边读取到"末尾"
                    break;
                }
                String request = scanner.next();

                //2.根据请求计算响应
                String response = process(request);

                //3.把响应写会给客户端
                //outputStream.write(response.getBytes());

                //此处可以按照字节数组来直接写,也可以有另外一种写法
                printWriter.println(response);

                /*这里这个很重要
                这里如果没有这个flush,服务器没办法响应客户端的请求,
                客户端的请求都在缓冲区内,利用这个清理
                 */
                printWriter.flush();

                //4.打印日志
                System.out.printf("[%s : %d] req = %s; resp = %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                        request,response);
            }
        }catch(IOException e){
            e.printStackTrace();//打印异常日志
        }
        /*
        这里的 clientSocket 是"连接级别"的数据,随着客户端断开连接了,这个socket也就不再使用了.
        (即使是同一个客户端,断开之后重新连接,也是一个新socket,和旧socket不是同一个了)
        因此,这样的socket就应该主动关闭掉 防止文件资源泄露
         */
        clientSocket.close();
    }

    private String process(String request) {
        //直接返回请求
        return request;
    }

    public static void main(String[] args) throws IOException {
        //创建服务器
        TcpEchoServer server  = new TcpEchoServer(9090);//给一个端口号
        server .start();
    }
}

/**
 * 这里补充一下如果是一个服务器对应多个客户端的代码
 *
 * 在每个while中创建线程
 * //创建线程,每个线程中创建个一个客户端
 * Thread t = new Thread(()->{
 *     try {
 *         processConnection(clientSocket);
 *      } catch (IOException e) {
 *          throw new RuntimeException(e);
 *      }
 * });
 * t.start();
 *
 *如果要使用线程池的话
 * ExecutorService service = Executor.newCachedThreadPool():
 * while(true){
 *     Socket clientSocket = new serverSocket.accept();
 *
 *     //使用线程池
 *     service.submit(()->{
 *         try {
 *              processConnection(clientSocket);
 *         } catch (IOException e) {
 *               throw new RuntimeException(e);
 *         }
 *     });
 * }
 */
